#include "test_byte.h"
#include "string.h"
#include "stdlib.h"
#include "esp_log.h"


#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"

#include "esp_timer.h"
#include "esp_log.h"
#include "esp_sleep.h"

#include "lua_user_model.h"



#define KEEPALIVE_IDLE              6
#define KEEPALIVE_INTERVAL          5
#define KEEPALIVE_COUNT             3

#define MODEL_NAME "socket"
typedef struct {
    int serverSock;//服务器socket
    int type; //类型 1服务器，2客户端

    int keepAlive;
    int keepIdle;
    int keepInterval;
    int keepCount;
    struct sockaddr_storage dest_addr;
}user_socket;


static int lua_user_bind(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int port=luaL_checkinteger(L,2);
    int listen_sock=(*temp)->serverSock;
    //绑定端口
    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&((*temp)->dest_addr);
    dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr_ip4->sin_family = AF_INET;
    dest_addr_ip4->sin_port = htons(port);

    int err = bind(listen_sock, (struct sockaddr *)&((*temp)->dest_addr), sizeof(struct sockaddr_storage));
    lua_pushinteger(L,err);
    return 1;

}


static int lua_user_connect(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    const char*ip=luaL_checkstring(L,2);
    int port=luaL_checkinteger(L,3);
    printf("%s , %d \r\n",ip,port);
    printf(" size:%d , %d\r\n",sizeof(struct sockaddr_in),sizeof(struct sockaddr_storage));
    int sock=(*temp)->serverSock;
    //绑定端口
    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&((*temp)->dest_addr);
    inet_pton(AF_INET, ip, &dest_addr_ip4->sin_addr);
    dest_addr_ip4->sin_family = AF_INET;
    dest_addr_ip4->sin_port = htons(port);
    int err=connect(sock,(struct sockaddr *)&((*temp)->dest_addr), sizeof(struct sockaddr_storage));
    lua_pushinteger(L,err);
    return 1;

}

static int lua_user_listen(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int listen_sock=(*temp)->serverSock;
    int argc=lua_gettop(L);
    int size=1;
    if(argc>1){
        size=luaL_checkinteger(L,2);
    }
    int err = listen(listen_sock, size);
    lua_pushinteger(L,err);
    return 1;
}
static int lua_user_accept(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int listen_sock=(*temp)->serverSock;
    char addr_str[128];



    user_socket*sockClient=(user_socket*)malloc(sizeof(user_socket));
    printf("p-> %p\r\n",sockClient);

    socklen_t addr_len = sizeof(sockClient->dest_addr);
    int sock = accept(listen_sock, (struct sockaddr *)&sockClient->dest_addr, &addr_len);

    sockClient->serverSock=sock;
    sockClient->type=2;
    sockClient->keepAlive = 1;
    sockClient->keepIdle = KEEPALIVE_IDLE;
    sockClient->keepInterval = KEEPALIVE_INTERVAL;
    sockClient->keepCount = KEEPALIVE_COUNT;

    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &sockClient->keepAlive, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &sockClient->keepIdle, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &sockClient->keepInterval, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &sockClient->keepCount, sizeof(int));

    if (sockClient->dest_addr.ss_family == PF_INET) {
        inet_ntoa_r(((struct sockaddr_in *)&sockClient->dest_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
    }
     printf("lua_user_accept socket:%d\r\n",sock);

    //接受完生成一个新的socket对象
    user_socket**tempClient = (user_socket**)lua_newuserdata(L, sizeof(user_socket*));
    *tempClient=sockClient;
    //*temp=tm;
    //Lua->stack，得到全局元表位置-1,userdata位置-2
    luaL_getmetatable(L, MODEL_NAME);
    //将元表赋值给位置-2的userdata，并弹出-1的元表
    lua_setmetatable(L, -2);
    return 1;
}



static int lua_user_clean(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int listen_sock=(*temp)->serverSock;
    close(listen_sock);
    return 0;
}

// tostring 方法 
static int lua_user_tostring(lua_State *L){
    lua_pushstring(L,"lua socket model");
    return 1;
}

// 执行 collectgarbage()函数会进行gc
static int lua_user_gc(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    if((*temp)->serverSock!=0){
        printf("---close()\r\n");
        close((*temp)->serverSock);
        shutdown((*temp)->serverSock,0);
    }
    printf("gc .. %d %p\r\n",(*temp)->serverSock,*temp);
    
    free(*temp);
    return 0;
}


static int lua_create_shutdown(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int listen_sock=(*temp)->serverSock;
    shutdown(listen_sock, 0);
    return 0;
}
//关闭
static int lua_create_close(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int sock=(*temp)->serverSock;
    close(sock);
    (*temp)->serverSock=0;
    return 0;
}
//接受
static int lua_create_recv(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int sock=(*temp)->serverSock;
    user_byte** bbt=(user_byte**)lua_touserdata(L,2);
    int len=recv(sock,(*bbt)->buff,(*bbt)->size,0);
    lua_pushinteger(L,len);

    return 1;
}
// 发送
static int lua_create_send(lua_State *L){
    user_socket**temp=(user_socket**)lua_touserdata(L,1);
    int sock=(*temp)->serverSock;
    int type=lua_type(L,2);
    int r=0;
    if(type==LUA_TSTRING){
        const char* str=luaL_checkstring(L,2);
        r=send(sock,str,strlen(str),0);
    }else{
       user_byte** bbt=(user_byte**)lua_touserdata(L,2);
       r=send(sock,(*bbt)->buff,(*bbt)->size,0);
    }
    if(r<0){
        ESP_LOGE("TAG", "Unable to accept connection: errno %d", errno);
    }
    lua_pushinteger(L,r);
    return 1;
}

static const luaL_Reg lua_user_func[] ={ 
    {"bind", lua_user_bind},
    {"connect", lua_user_connect},
    {"listen", lua_user_listen},
    {"accept", lua_user_accept},

    { "close", lua_create_close },
    { "shutdown", lua_create_shutdown },
    { "recv", lua_create_recv },
    { "send", lua_create_send },

    {"clean", lua_user_clean},
    {"__tostring", lua_user_tostring}, 
    {"__gc", lua_user_gc}, 
	{NULL, NULL}
};


// Timer.create('time1',22,call)
static int lua_create_new_server(lua_State *L){
     //创建一个对象指针放到stack里，返回给Lua中使用，userdata的位置-1


    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    user_socket*sock=(user_socket*)malloc(sizeof(user_socket));
    printf("p-> %p\r\n",sock);
    sock->serverSock=listen_sock;
    sock->type=1;
    sock->keepAlive = 1;
    sock->keepIdle = KEEPALIVE_IDLE;
    sock->keepInterval = KEEPALIVE_INTERVAL;
    sock->keepCount = KEEPALIVE_COUNT;
    
    user_socket**temp = (user_socket**)lua_newuserdata(L, sizeof(user_socket*));
    *temp=sock;
    //*temp=tm;
    //Lua->stack，得到全局元表位置-1,userdata位置-2
    luaL_getmetatable(L, MODEL_NAME);
    //将元表赋值给位置-2的userdata，并弹出-1的元表
    lua_setmetatable(L, -2);
    return 1;
}


// Timer.create('time1',22,call)
static int lua_create_new_client(lua_State *L){
     //创建一个对象指针放到stack里，返回给Lua中使用，userdata的位置-1
     
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    user_socket*sock=(user_socket*)malloc(sizeof(user_socket));
    printf("p-> %p\r\n",sock);
    sock->serverSock=listen_sock;
    sock->type=1;
    sock->keepAlive = 0;
    sock->keepIdle = KEEPALIVE_IDLE;
    sock->keepInterval = KEEPALIVE_INTERVAL;
    sock->keepCount = KEEPALIVE_COUNT;
    
    user_socket**temp = (user_socket**)lua_newuserdata(L, sizeof(user_socket*));
    *temp=sock;
    //*temp=tm;
    //Lua->stack，得到全局元表位置-1,userdata位置-2
    luaL_getmetatable(L, MODEL_NAME);
    //将元表赋值给位置-2的userdata，并弹出-1的元表
    lua_setmetatable(L, -2);
    return 1;
}



static const luaL_Reg lua_user_constructor_funcs[] = {
    { "server", lua_create_new_server },
    { "client", lua_create_new_client },
    
    { NULL, NULL }
};

int luaopen_user_socket_libs (lua_State *L) {
     //创建全局元表（里面包含了对LUA_REGISTRYINDEX的操作），元表的位置为-1
    luaL_newmetatable(L, MODEL_NAME);

    //将元表作为一个副本压栈到位置-1，原元表位置-2
    lua_pushvalue(L, -1);

    //设置-2位置元表的__index索引的值为位置-1的元表，并弹出位置-1的元表，原元表的位置为-1
    lua_setfield(L, -2, "__index");

    //将成员函数注册到元表中（到这里，全局元表的设置就全部完成了）
    luaL_setfuncs(L, lua_user_func, 0);
    //注册构造函数到新表中，并返回给Lua
    luaL_newlib(L, lua_user_constructor_funcs);
    return 1;
}